/* Copyright (c) 2004-2008, Andriy Nikolov
 * All rights reserved.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
package org.processmining.email;

import java.util.*;
import java.io.*;
import javax.swing.*;
import java.text.*;

/**
 * <p> Represents a single process case (process instance) mined from the e-mail
 * log.
 *
 * @author Andriy Nikolov
 * @version 1.0
 */
public class ProcessCase {
  LinkedList taskList;
  String caseDescription;
  LinkedList messageList;
  LinkedList eventList;
  Hashtable taskMap;
  Hashtable participants;
  Vector aliases;
  ProcessLog wholeLog;

  /**
   * Creates a ProcessCase instance and sets the reference to the ProcessLog
   * instance.
   *
   * @param log ProcessLog
   */
  public ProcessCase(ProcessLog log) {
    taskList = new LinkedList();
    messageList = new LinkedList();
    taskMap = new Hashtable();
    eventList = new LinkedList();
    participants = new Hashtable();
    aliases = new Vector();
    wholeLog = log;
  }

  /**
   * Creates a ProcessCase instance with a given name and sets the reference to
   * the ProcessLog instance.
   *
   * @param description String
   * @param log ProcessLog
   */
  public ProcessCase(String description, ProcessLog log) {
    taskList = new LinkedList();
    messageList = new LinkedList();
    taskMap = new Hashtable();
    eventList = new LinkedList();
    participants = new Hashtable();
    caseDescription = description;
    wholeLog = log;
    aliases = new Vector();
    aliases.add(description);
  }

  /**
   * Returns all alias names of the process instance.
   *
   * @return Vector
   */
  public Vector getAliases() {
    return aliases;
  }

  /**
   * Gets an alias name with a given index.
   *
   * @param i int
   * @return String
   */
  public String getAlias(int i) {
    return (String)aliases.get(i);
  }

  /**
   * Adds an alias name for the process case.
   *
   * @param alias String
   */
  public void addAlias(String alias) {
    if(!hasAlias(alias))
      aliases.add(alias);
  }

  /**
   * Sets the reference on the ProcessLog object.
   *
   * @param log ProcessLog
   */
  public void setProcessLog(ProcessLog log) {
    wholeLog = log;
  }

  /**
   * Gets the process log, to which the case relates.
   *
   * @return ProcessLog
   */
  public ProcessLog getProcessLog() {
    return wholeLog;
  }

  /**
   * Sets the main name of the process instance.
   *
   * @param description String
   */
  public void setDescription(String description) {
    caseDescription = description;
    if (!aliases.contains(description))
      aliases.add(description);
  }

  /**
   * Gets the main name of the process case.
   *
   * @return String
   */
  public String getDescription() {
    return caseDescription;
  }

  /**
   * Writes the case information into the writer. Used only by the ProcessLog
   * instances.
   *
   * @param writer PrintWriter
   * @param index int
   */
  public void save(PrintWriter writer, int index) {
    ProcessEvent iterEvent;
    writer.println("<ProcessInstance id=\""+index+"\" description=\""+refineString(caseDescription)+"\" >");
    for(int i=0;i<eventList.size();i++) {
      iterEvent = (ProcessEvent)eventList.get(i);
      iterEvent.save(writer);
    }
    writer.println("</ProcessInstance>");
  }

  /**
   * Specifies that a certain e-mail message is related to the current case.
   *
   * @param message EMailMessage
   */
  public void addMessage(EMailMessage message) {
    messageList.add(message);
  }

  /**
   * Checks if the given alias is in the list of all alias names.
   *
   * @param alias String
   * @return boolean
   */
  public boolean hasAlias(String alias) {
    String tmp;
    for(int i=0;i<aliases.size();i++) {
      tmp = (String)aliases.get(i);
      if(tmp.equalsIgnoreCase(alias)) {
        return true;
      }
    }
    return false;
  }

  public boolean removeAlias(int index) {
    String tmp;
    tmp = (String)aliases.get(index);
    if(!tmp.equals(caseDescription)) {
      aliases.remove(index);
      return true;
    } else {
      return false;
    }
  }

  /**
   * Mines the information about the process instance from the list of related
   * messages.
   */
  public void build() {
    ProcessTask iterTask;
    Collections.sort(messageList, new TimestampOwnerComparator());
    if (wholeLog.getTaskTypesList().size()==0)
      wholeLog.extractPossibleTaskTypes();
    extractAllTaskTypes();
    extractAllParticipants();
    for(int i=0;i<taskList.size();i++) {
      iterTask = (ProcessTask)taskList.get(i);
      iterTask.build();
    }
    Collections.sort(eventList, new TimestampOwnerComparator());
  }

  private void extractAllTaskTypes() {
    EMailMessage iterMessage;
    ProcessTask task;
    ProcessTaskType curTaskType, taskType;
    String tmpName;
    int i, j, k;

    for(i=0;i<messageList.size();i++) {
      iterMessage = (EMailMessage)messageList.get(i);
      taskType = null;
      switch(wholeLog.getTaskSourceType()) {
        case ProcessLog.TASK_SUBJECT:
          for(j=0;j<wholeLog.getTaskTypesList().size();j++) {
            curTaskType = (ProcessTaskType) wholeLog.getTaskType(j);
            for (k = 0; k < curTaskType.getAliases().size(); k++) {
              tmpName = curTaskType.getAlias(k);
              if (wholeLog.isPartialCoincidenceTasksAllowed()) {
                if (iterMessage.getSubject().toLowerCase().indexOf(tmpName.
                    toLowerCase()) != -1) {
                  taskType = curTaskType;
                  break;
                }
              }
              else {
                if (iterMessage.getSubject().toLowerCase().equals(tmpName.
                    toLowerCase())) {
                  taskType = curTaskType;
                  break;
                }
              }
            }
          }
          break;
        default:
          if((!iterMessage.getTask().equals(""))&&(iterMessage.getTask()!=null)) {
            for(j=0;j<wholeLog.getTaskTypesList().size();j++) {
              curTaskType = wholeLog.getTaskType(j);
              for(k=0;k<curTaskType.getAliases().size();k++) {
                tmpName = curTaskType.getAlias(k);
                if(iterMessage.getTask().equals(tmpName)) {
                  taskType = curTaskType;
                }
              }
            }
          }
          break;
      }
      if(taskType!=null) {
        task = (ProcessTask)taskMap.get(taskType);
        if(task==null) {
          task = new ProcessTask(taskType, this);
          taskList.add(task);
          taskMap.put(taskType, task);
        }
        task.addMessage(iterMessage);
      }
    }
  }

  private void extractAllParticipants() {
    EMailMessage iterMessage;
    EMailRecipient participant;
    Vector recList;
    String name;
    for(int i=0;i<messageList.size();i++) {
      iterMessage = (EMailMessage)messageList.get(i);
      name = iterMessage.getFrom().getName();
      if(!participants.containsKey(name)) {
        participant = iterMessage.getFrom();
        participants.put(participant.getName(), participant);
      }
      recList = (Vector)iterMessage.getTo();
      for (int j=0;j<recList.size();j++) {
        participant = (EMailRecipient)recList.get(j);
        name = participant.getName();
        if(!participants.containsKey(name)) {
          participants.put(name, participant);
        }
      }
      recList = (Vector)iterMessage.getCc();
      for (int j=0;j<recList.size();j++) {
        participant = (EMailRecipient)recList.get(j);
        name = participant.getName();
        if(!participants.containsKey(name)) {
          participants.put(name, participant);
        }
      }
      recList = (Vector)iterMessage.getBcc();
      for (int j=0;j<recList.size();j++) {
        participant = (EMailRecipient)recList.get(j);
        name = participant.getName();
        if(!participants.containsKey(name)) {
          participants.put(name, participant);
        }
      }
    }
  }

  /**
   * Gets the list of events, which constitute the process instance. Must be
   * called after build().
   *
   * @return LinkedList
   */
  public LinkedList getEventList() {
    return eventList;
  }

  /**
   * Returns the hashtable of recipients related to the case.
   *
   * @return Hashtable - a hashtable where keys are recipient names and items
   *   are EMailRecipient instances.
   */
  public Hashtable getParticipants() {
    return this.participants;
  }

  /**
   * Adds an event into the event list.
   *
   * @param event ProcessEvent
   */
  public void addEvent(ProcessEvent event) {
    eventList.add(event);
  }

  /**
   * Writes a report about the case into the JTextArea
   *
   * @param textArea JTextArea
   */
  public void write(JTextArea textArea) {
    ProcessEvent iterEvent;
    textArea.append("Case: "+caseDescription+"\n");
    for(int i=0;i<eventList.size();i++) {
      iterEvent = (ProcessEvent)eventList.get(i);
      iterEvent.write(textArea);
    }
    textArea.append("Case "+caseDescription+" finished\n");
  }

  private String refineString(String str) {
    String tmp = str;
    tmp = tmp.replaceAll("&", "&amp;");
    tmp = tmp.replaceAll("<", "&lt;");
    tmp = tmp.replaceAll(">", "&gt;");
    tmp = tmp.replaceAll("\"", "&quot;");
    tmp = tmp.replaceAll("\'", "&apos;");
    return tmp;
  }

  /**
   * Clears all mined information about the process case.
   */
  public void clear() {
    taskList.clear();
    messageList.clear();
    eventList.clear();
    taskMap.clear();
    participants.clear();
  }

  /**
   * Writes the process instance information into the writer in the standard Dot
   * format for future visualization.
   *
   * @param bw Writer
   * @throws IOException
   */
  public void writeToDot(Writer bw) throws IOException {
    ProcessEvent ate;
    bw.write("digraph G {fontsize=\"8\"; remincross=true;");
    bw.write("fontname=\"Arial\";rankdir=\"TB\";\n");
    bw.write("edge [arrowsize=\"0.7\",fontname=\"Arial\",fontsize=\"8\"];\n");
    bw.write("node [height=\".2\",width=\".2\",fontname=\"Arial\",fontsize=\"8\"];\n");


    bw.write("pidata [shape=\"box\",rank=\"source\",label=\"Process Instance Data:");

/*    it = getData().keySet().iterator();
    while (it.hasNext()) {
      String key = (String) it.next();
      bw.write("\\n" + key + " = "+ (String) getData().get(key));
    }*/
    bw.write("\"];\n");

//    int i=0;
//    while (ates.hasNext()) {
    for(int i=0;i<eventList.size();i++) {
      ate = (ProcessEvent)eventList.get(i);
      Date d = ate.getTimestamp();
      String s="";
      if (d!=null) {
        SimpleDateFormat dateParser = new SimpleDateFormat("Z");
        String timezone = dateParser.format(d);
        timezone = timezone.substring(0, 3) + ":" + timezone.substring(3, 5);
        dateParser = new SimpleDateFormat("yyyy-MM-dd  HH:mm:ss.SSS  ");
        s = dateParser.format(d);
        s += timezone;
      }

      bw.write("ate"+i+" [shape=\"box\",label=\""+ate.getTask().getType().getName()+"\\n"+ate.getType().getAliasName()+"\\n"+s+"\"];\n");

      bw.write("atedata"+i+" [shape=\"box\",label=\"");
      bw.write("Originator = "+ ate.getOriginator()+"\\n");

/*      it = ate.getData().keySet().iterator();
      while (it.hasNext()) {
        String key = (String) it.next();
        bw.write(key+" = "+ (String) ate.getData().get(key)+"\\n");
      }*/
      bw.write("\"];\n");
      bw.write("ate"+i+" -> atedata"+i+";");
      if (i>0) {
        bw.write("ate"+(i-1)+" -> ate"+i+";");
      }
      bw.write("{ rank = same; "+"ate"+i+";atedata"+i+";}");
    }
    bw.write("pidata -> ate0 [color=\"white\"];\n");
    bw.write("}\n");
  }


}
